﻿using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace KeyboardCountService
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x0101;

        private LowLevelKeyboardProc _proc;
        private IntPtr _hookID = IntPtr.Zero;

        public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        [StructLayout(LayoutKind.Sequential)]
        private struct KBDLLHOOKSTRUCT
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public IntPtr dwExtraInfo;
        }

        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            WriteLog(nCode.ToString()+ " " + wParam.ToString() + " " + lParam.ToString());
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)//&& wParam == (IntPtr)WM_KEYDOWN ||
            {
                KBDLLHOOKSTRUCT hookStruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
                //Console.WriteLine($"Key: {hookStruct.vkCode} {(wParam == (IntPtr)WM_KEYDOWN ? "Pressed" : "Released")}");
                var key = GetCharFromVirtualKeyCode(hookStruct);
                if (key != null)
                {
                    WriteLog(key);
                }
            }

            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        private IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            {
                WriteLog(curProcess.MainModule.ModuleName);
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curProcess.MainModule.ModuleName), 0);
            }
        }

        private string GetCharFromVirtualKeyCode(KBDLLHOOKSTRUCT hookStruct)
        {
            //bool isShiftPressed = (ModifierKeys & Keys.Shift) == Keys.Shift;
            var key = (Keys)hookStruct.vkCode;
            var keyString = key.ToString();
            //if (keyString.Length > 0)
            //{
            //    return char.ToUpper(keyString[0]) + keyString.Substring(1).ToLower();
            //}
            //else if (isShiftPressed)
            //{
            //    return keyString.ToUpper();
            //}
            //else
            //{
            //    return keyString.ToLower();
            //}
            return keyString;
        }

        private void WriteLog(string msg)
        {
            File.AppendAllText($"log-{DateTime.Now.ToString("yyyyMMdd")}.txt", msg + Convert.ToChar(1));
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            LowLevelKeyboardProc _proc = HookCallback;
            _hookID = SetHook(_proc);
            WriteLog("keyboard hook installed");
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnhookWindowsHookEx(_hookID);
            _hookID = IntPtr.Zero;
            WriteLog("keyboard hook uninstalled");
        }
    }
}
